from editor_base import EditorBase
from categories import CategoryEditor
from attributes import AttributeEditor

from ..bases import widgets


class DeviceEditor(EditorBase):
    topwidget_label = 'Device'
    def init_editor(self, **kwargs):
        self.device = kwargs.get('device')
        self.attribute_editor = None
        
        hbox = widgets.HBox()
        vbox = widgets.VBox()
        keys = ('Name', 'Manufacturer', 'Description', 'Personality Mode')
        attrs = ['name', 'manufacturer', 'description', 'personality_mode']
        self.device_prop_attr_map = dict(zip(keys, attrs))
        self.device_prop_keys = keys
        names = ['Property', 'Value']
        types = [str, str]
        editable = [1]
        order = [0, 1]
        self.device_prop_list = widgets.TreeList(name='Device Properties', column_names=names, 
                                                 list_types=types, column_order=order, 
                                                 columns_editable=editable)
        self.update_device_prop_list()
        self.device_prop_list.connect('cell_edited', self.on_device_prop_list_cell_edited)
        vbox.pack_start(self.device_prop_list.topwidget, expand=True)
        self.sbPanTiltRange = {}
        if 'Mover' in [cat.name for cat in self.device.categories.values()]:
            poskwargs = {'value_max':999}
            for key in ['pan_range', 'tilt_range']:
                if self.device.saved_class_name == 'DefinedDevice':
                    obj = self.device
                else:
                    obj = self.device.Groups['Position']
                poskwargs['Property'] = obj.Properties[key]
                poskwargs['label'] = ' '.join(key.split('_')).capitalize()
                self.sbPanTiltRange[key] = widgets.SpinBtn(**poskwargs)
                vbox.pack_start(self.sbPanTiltRange[key].topwidget)
        
        self.sbChan = widgets.SpinBtn(label='Channel Count', Property=(self.device, 'channel_count'))
        vbox.pack_start(self.sbChan.topwidget)
        
        hbox.pack_start(vbox, expand=True)
        
        obj = self.make_child_editor(CategoryEditor, 'categories', src_object=self.device)
        hbox.pack_start(obj.topwidget, expand=True)
        
        self.topwidget.pack_start(hbox, expand=True)
        
        box = widgets.HBox()
        self.column_attr_map = {'device':{0:'id', 1:'name', 2:'chan_index'}, 
                                'global':{0:'id', 1:'name', 2:'name'}}
        names = ['id', 'Name', 'Type']
        types = [str, str, str]
        order = [1, 2]
        
        self.global_attribute_list = widgets.TreeList(name='Global Attributes', column_names=names, 
                                                      list_types=types, column_order=order)
        self.update_global_attribute_list()
        self.global_attribute_list.connect('selection_changed', self.on_global_attribute_list_selection_changed)
        hbox2 = widgets.HBox()
        self.btn_add_attribute = widgets.Button(label='Add Attribute')
        self.btn_add_attribute.connect('clicked', self.on_btn_add_attribute_clicked)
        hbox2.pack_start(self.btn_add_attribute)
        self.btn_del_attribute = widgets.Button(label='Delete Attribute')
        self.btn_del_attribute.connect('clicked', self.on_btn_del_attribute_clicked)
        hbox2.pack_start(self.btn_del_attribute)
        self.global_attribute_list.topwidget.pack_start(hbox2)
        box.pack_start(self.global_attribute_list.topwidget, expand=True)
        
        names = ['id', 'Name', 'Channel']
        types = [str, str, int]
        order = [1, 2]
        editable = [1, 2]
        
        self.device_attribute_list = widgets.TreeList(name='Device Attributes', column_names=names, default_sort_column=2, 
                                               list_types=types, column_order=order, columns_editable=editable)
        self.update_device_attribute_list()
        self.device_attribute_list.connect('selection_changed', self.on_device_attribute_list_selection_changed)
        self.device_attribute_list.connect('cell_edited', self.on_device_attribute_list_cell_edited)
        box.pack_start(self.device_attribute_list.topwidget, expand=True)
        
        self.topwidget.pack_start(box, expand=True)
        self.hbox = box
        
        self.DeviceSystem.bind(**dict(zip(['new_attribute', 'del_attribute'], [self.on_GlobalAttributes_child_update]*2)))
        self.device.Attributes.bind(**dict(zip(['child_added', 'child_removed'], [self.on_Attributes_child_update]*2)))
        self.device.bind(property_changed=self.on_device_property_changed)
        #for attr in self.device_prop_attr_map.itervalues():
        #    self.device.connect('attr_watch_%s' % (attr), self.on_device_property_changed)
        
        
    def unlink(self):
        super(DeviceEditor, self).unlink()
        self.DeviceSystem.unbind(self.on_GlobalAttributes_child_update)
        for attrib in self.DeviceSystem.GlobalAttributes.itervalues():
            attrib.unbind(self.on_global_attribute_property_changed)
        for attrib in self.device.Attributes.itervalues():
            attrib.unbind(self.on_device_attribute_property_changed)
        self.sbChan.unlink()
        for sb in self.sbPanTiltRange.itervalues():
            sb.unlink()
        self.device.unbind(self.on_device_property_changed)
        self.device.Attributes.unbind(self.on_Attributes_child_update)
        
        
    def update_device_prop_list(self, key=None):
        if key:
            attr = self.device_prop_attr_map[key]
            self.device_prop_list.update({key:getattr(self.device, attr)})
        else:
            for key in self.device_prop_keys:
                attr = self.device_prop_attr_map[key]
                self.device_prop_list.update({key:getattr(self.device, attr)})
    
    def update_global_attribute_list(self, key=None):
        if key is not None:
            attributes = {key:self.DeviceSystem.GlobalAttributes[key]}
            #obj = self.DeviceSystem.GlobalAttributes[key]
            #values = [getattr(obj, self.column_attr_map['global'][x]) for x in [1, 2]]
            #self.global_attribute_list.update({key:values})
        else:
            attributes = self.DeviceSystem.GlobalAttributes
        for key, val in attributes.iteritems():
            values = [getattr(val, self.column_attr_map['global'][x]) for x in [1, 2]]
            self.global_attribute_list.update({key:values})
            val.bind(property_changed=self.on_global_attribute_property_changed)
                
    def update_device_attribute_list(self, key=None):
        if key is not None:
            attributes = {key:self.device.Attributes[key]}
            #obj = self.device.Attributes[key]
            #values = [getattr(obj, self.column_attr_map['device'][x]) for x in [1, 2]]
            #self.device_attribute_list.update({key:values})
        else:
            attributes = self.device.Attributes
        for key, val in attributes.iteritems():
            values = [getattr(val, self.column_attr_map['device'][x]) for x in [1, 2]]
            self.device_attribute_list.update({key:values})
            val.bind(property_changed=self.on_device_attribute_property_changed)
                
    def on_device_prop_list_cell_edited(self, **kwargs):
        key = kwargs.get('key')
        value = kwargs.get('value')
        attr = self.device_prop_attr_map[key]
        setattr(self.device, attr, value)
    
    def on_device_attribute_list_cell_edited(self, **kwargs):
        key = kwargs.get('key')
        column = kwargs.get('column')
        value = kwargs.get('value')
        attr = self.column_attr_map['device'][column]
        setattr(self.device.Attributes[key], attr, value)
                
    def on_global_attribute_list_selection_changed(self, **kwargs):
        pass
        
    def on_device_attribute_list_selection_changed(self, **kwargs):
        key = kwargs.get('key')
        if self.attribute_editor is not None:
            self.attribute_editor.unlink()
            self.hbox.remove(self.attribute_editor.topwidget)
            self.attribute_editor = None
        attribute = self.device.Attributes.get(key)
        if attribute:
            obj = self.make_child_editor(AttributeEditor, 'attribute', attribute=attribute)
            self.hbox.pack_start(obj.topwidget, expand=True)
            self.attribute_editor = obj
        
    def on_btn_add_attribute_clicked(self, btn):
        key = self.global_attribute_list.current_selection
        attr = self.DeviceSystem.GlobalAttributes[key]
        if attr is not None:
            dev_attr = self.device.add_attribute(attribute=attr)
            self.update_device_attribute_list(dev_attr.id)
        
    def on_btn_del_attribute_clicked(self, btn):
        key = self.device_attribute_list.current_selection
        if key:
            self.device.del_attribute(id=key)
            #self.device_attribute_list.clear()
            #self.update_device_attribute_list()
            
    def on_GlobalAttributes_child_update(self, **kwargs):
        key = kwargs.get('id')
        if kwargs.get('type') != 'GlobalAttribute':
            return
        if kwargs.get('signal_name') == 'del_attribute':
            self.global_attribute_list.clear()
            key = None
        self.update_global_attribute_list(key)
        
    def on_Attributes_child_update(self, **kwargs):
        key = kwargs.get('obj').id
        if kwargs.get('signal_name') == 'child_removed':
            self.device_attribute_list.clear()
            key = None
        self.update_device_attribute_list(key)
        
    def on_device_property_changed(self, **kwargs):
        name = kwargs.get('name')
        for key, val in self.device_prop_attr_map.iteritems():
            if val in name:
                self.update_device_prop_list(key)
        
    def on_global_attribute_property_changed(self, **kwargs):
        attrib = kwargs.get('obj')
        self.update_global_attribute_list(attrib.id)
        
    def on_device_attribute_property_changed(self, **kwargs):
        attrib = kwargs.get('obj')
        self.update_device_attribute_list(attrib.id)

class PatchedDeviceEditor(EditorBase):
    topwidget_label = 'Device'
    _Properties = {'device_number':dict(type=int)}
    def init_editor(self, **kwargs):
        self.device = kwargs.get('device')
        self.device_number = self.device.device_num
        self._start_chan = self.device.start_chan
        vbox = widgets.VBox()
        ## TODO: make it find properties in the attribute groups
        keys = ('Name', 'Description', 'Pan Invert', 'Tilt Invert')
        attrs = ['name', 'description', 'pan_invert', 'tilt_invert']
        self.device_prop_attr_map = dict(zip(keys, attrs))
        self.device_prop_keys = keys
        names = ['Property', 'Value', 'Active', 'cell_active']
        types = [str, str, bool, bool]
        editable = [1, 2]
        order = [0, 1, 2]
        self.device_prop_list = widgets.TreeList(name='Device Properties', column_names=names, 
                                                 list_types=types, column_order=order, 
                                                 columns_editable=editable)
        self.update_device_prop_list()
        self.device_prop_list.connect('cell_edited', self.on_device_prop_list_cell_edited)
        self.device_prop_list.connect('cell_toggled', self.on_device_prop_list_cell_toggled)
        vbox.pack_start(self.device_prop_list.topwidget, expand=True)
        
        hbox = widgets.HBox()
        self.sbDevNum = widgets.SpinBtn(label='Device Number', Property=(self, 'device_number'))
        hbox.pack_start(self.sbDevNum.topwidget)
        btn = widgets.Button(label='Update')
        btn.connect('clicked', self.on_btn_device_number_update_clicked)
        hbox.pack_start(btn)
        vbox.pack_start(hbox)
        
        hbox = widgets.HBox()
        self.universe_combo = UniverseCombo(MainController=self.MainController)
        self.universe_combo.set_current_selection(key=self.device.universe_id)
        self.universe_combo.connect('selection_changed', self.on_universe_combo_selection_changed)
        hbox.pack_start(self.universe_combo.topwidget)
        self.sbStartChan = widgets.SpinBtn(label='Start Channel', src_object=self, 
                                           src_attr='start_chan', value_max=512)
        hbox.pack_start(self.sbStartChan.topwidget)
        btn = widgets.Button(label='Repatch')
        btn.connect('clicked', self.on_btn_repatch_clicked)
        hbox.pack_start(btn)
        vbox.pack_start(hbox)
        
        self.topwidget.pack_start(vbox, expand=True)
        
        self.device.bind(property_changed=self.on_device_property_changed)
        #for attr in self.device_prop_attr_map.itervalues():
        #    self.device.connect('attr_watch_%s' % (attr), self.on_device_property_changed)
        
    def unlink(self):
        super(PatchedDeviceEditor, self).unlink()
        self.device.unbind(self.on_device_property_changed)
        
    @property
    def start_chan(self):
        return self._start_chan
    @start_chan.setter
    def start_chan(self, value):
        self._start_chan = int(value)
        
    def update_device_prop_list(self, key=None):
        if key:
            props = [key]
        else:
            props = self.device_prop_attr_map
        for key in props:
            attr = self.device_prop_attr_map[key]
            value = getattr(self.device, attr)
            if type(value) == bool:
                row = ['', value, True]
            else:
                row = [value, False, False]
            self.device_prop_list.update({key:row})
    
    def on_btn_device_number_update_clicked(self, btn):
        self.device.device_num = self.device_number
        self._device_number = self.device.device_num
        self.sbDevNum.on_Property_value_changed()
    
    def on_device_prop_list_cell_edited(self, **kwargs):
        key = kwargs.get('key')
        value = kwargs.get('value')
        attr = self.device_prop_attr_map[key]
        setattr(self.device, attr, value)
        
    def on_device_prop_list_cell_toggled(self, **kwargs):
        key = kwargs.get('key')
        state = kwargs.get('state')
        attr = self.device_prop_attr_map[key]
        setattr(self.device, attr, state)
        
    def on_device_property_changed(self, **kwargs):
        prop = kwargs.get('Property')
        if prop.name == 'Index':
            self.device_number = prop.value
        for key, val in self.device_prop_attr_map.iteritems():
            if val in prop.name:
                self.update_device_prop_list(key)
            
    def on_universe_combo_selection_changed(self, **kwargs):
        pass
    
    def on_btn_repatch_clicked(self, btn):
        kwargs = dict(universe_id=self.universe_combo.current_selection, 
                      start_chan=self.start_chan, device=self.device)
        if None not in kwargs.values():
            self.DeviceSystem.repatch_device(**kwargs)
